﻿/*
 * Created by SharpDevelop.
 * User: Administrator
 * Date: 3/1/2011
 * Time: 8:30 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using TomatoSynchronizer.Entities;
using TomatoSynchronizer.Store;
using HtmlAgilityPack;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace TomatoSynchronizer.Synchronizer
{
	/// <summary>
	/// Description of CategorySynchronizer
	/// </summary>
	public static class CategorySynchronizer
	{
		public static void Synchronize(Site siteSrc,Site siteDest,HtmlWeb client,SyncDirection direction)
		{
			switch(direction)
			{
				case SyncDirection.Download:
					UpLoadItems(siteDest,siteSrc,client,direction);
					break;
				case SyncDirection.Upload:
					UpLoadItems(siteSrc,siteDest,client,direction);
					break;
				case SyncDirection.DownloadAndUpload:
					UpLoadItems(siteDest,siteSrc,client,SyncDirection.Download);
					UpLoadItems(siteSrc,siteDest,client,SyncDirection.Upload);
					break;
				case SyncDirection.UploadAndDownload:
					UpLoadItems(siteSrc,siteDest,client,SyncDirection.Upload);
					UpLoadItems(siteDest,siteSrc,client,SyncDirection.Download);
					break;
				case SyncDirection.Synchronize:
					bool synchro = false;
					CategoryStore CategoryStoreSrc = new CategoryStore(siteSrc,client);
					CategoryStore CategoryStoreDest = new CategoryStore(siteDest,client);
					CategoryStoreDest.Truncate();
					
					Category[] categoriesSrc =  CategoryStoreSrc.LoadItems();
					
					MappingStore mappingStore = new MappingStore(siteSrc,siteDest,"categories_id");
					
					foreach(Category categoriesrc in categoriesSrc)
					{
						if (categoriesrc.parent_id == "0")
						{
							synchro = UploadItemRecursive(categoriesSrc,CategoryStoreDest,categoriesrc,"0",mappingStore);
						}
					}
					
					mappingStore.Save();
					
					if(!synchro)
					{
						throw new Exception("Could not synchronize destination store");
					}
					break;
			}
		}
		
		private static void DownLoadItems(Site _siteSrc,Site _siteDest,HtmlWeb client,SyncDirection direction)
		{
			UpLoadItems(_siteDest,_siteSrc,client,direction);
		}
		
		private static void UpLoadItems(Site _siteSrc,Site _siteDest,HtmlWeb client,SyncDirection direction)
		{
			CategoryStore CategoryStoreSrc = new CategoryStore(_siteSrc,client);
			CategoryStore CategoryStoreDest = new CategoryStore(_siteDest,client);
			
			Category[] categoriesSrc =  CategoryStoreSrc.LoadItems();
			Category[] categoriesDest =  CategoryStoreDest.LoadItems();
			
			MappingStore mappingStoreSrc = new MappingStore(_siteSrc,_siteDest,"categories_id");
			MappingStore mappingStoreDest = new MappingStore(_siteDest,_siteSrc,"categories_id");
			
			mappingStoreSrc.Clean<Category>(CategoryStoreSrc,CategoryStoreDest);
			mappingStoreDest.Clean<Category>(CategoryStoreDest,CategoryStoreSrc);
			
			MappingStore mappingStore = new MappingStore(_siteSrc,_siteDest,"languages_id");						
			
			foreach(Category categoriesrc in categoriesSrc)
			{
				//we assume the Category is not yet synchronized
				bool synchronized = false;
				
				//we check if a mapping already exists for this Category in the destination store
				foreach(Mapping mapping in mappingStoreSrc.Mappings)
				{
					if(!synchronized)
					{
						if(mapping.SrcValue == categoriesrc.categories_id)
						{
							//this Category already exists in the destination store
							//we check if everything changed since the last synchronization
							Category CategoryDest = CategoryStoreDest.GetItem(mapping.DestValue);
							if(CategoryDest.categories_id != null)
							{
								synchronized = SynchronizeItem(categoriesrc,CategoryDest,direction,mappingStoreSrc);
							}
							else
							{
								//the mapping exists, but the categpry has been deleted in the destination store
								//we set a flag that this mapping does not exists anymore
								mapping.Exists = false;
							}
						}
					}
				}
				
				if(!synchronized)
				{
					//there is no mapping, we check if this Category already exists in the destination store
					foreach(Category CategoryDest in categoriesDest)
					{
						foreach(Mapping _mapping in mappingStore.Mappings)
						{
							try
							{
								if(categoriesrc.categories_names["categories_name[" + _mapping.SrcValue + "]"] == CategoryDest.categories_names["categories_name[" + _mapping.DestValue + "]"] && synchronized == false)
								{
									Mapping mapping= new Mapping(categoriesrc.categories_id,CategoryDest.categories_id,"categories_id");
									mappingStoreSrc.Mappings.Add(mapping);
									
									synchronized = SynchronizeItem(categoriesrc,CategoryDest,direction,mappingStoreSrc);
								}
							}
							catch (KeyNotFoundException)
							{}
						}
					}
				}
				
				if(!synchronized)
				{
					//no mapping the Category is new
					//we insert it in the destination store
					string remoteId = string.Empty;
					
					if (categoriesrc.parent_id == "0")
					{
						//we upload the Category tree
						synchronized = UploadItemRecursive(categoriesSrc,CategoryStoreDest,categoriesrc,"0",mappingStoreSrc);
					}
					else
					{
						//should be already uploaded, cause it's a child Category
						//a mapping should exists, else there is a problem
						Mapping mapping = mappingStoreSrc.GetMapping(categoriesrc.categories_id,"categories_id");
						
						if(mapping == null)
						{
							//not normal, we upload again
							Mapping parentMapping = mappingStoreSrc.GetMapping(categoriesrc.parent_id,"categories_id");
							
							if(parentMapping == null)
							{
								//throw new Exception("something is wrong, check your mapping store and the mapping property");
								//parent has not been synchronized yet, this will be synchronized with the parent...we skip
								synchronized = true;
							}
							else
							{
								synchronized = UploadItemRecursive(categoriesSrc,CategoryStoreDest,categoriesrc,parentMapping.DestValue,mappingStoreSrc);
							}
						}
						else
						{
							//the mapping exists, we load the destination Category
							Category CategoryDest = CategoryStoreDest.LoadItem(mapping.DestValue);
							if(CategoryDest != null)
							{
								//we update it
								synchronized = SynchronizeItem(categoriesrc,CategoryDest,direction,mappingStoreSrc);
							}
							else
							{
								//it has been deleted since the last sync
								mapping.Exists = false;
								Mapping parentMapping = mappingStoreSrc.GetMapping(categoriesrc.parent_id,"categories_id");
								
								if(parentMapping == null)
								{
									throw new Exception("something is wrong !!");
								}
								
								synchronized = UploadItemRecursive(categoriesSrc,CategoryStoreDest,categoriesrc,parentMapping.DestValue,mappingStoreSrc);
							}
						}
						
						if(!synchronized)
						{
							throw new Exception("The Category " + categoriesrc.categories_name + " has not been synchronized");
						}
						
						synchronized = true;
						
					}
				}
				
				if(!synchronized)
				{
					throw new Exception("could not synchronize the Category " + categoriesrc.categories_name);
				}
			}
			
			mappingStoreSrc.Save();
			
			mappingStoreDest.Truncate();
			
			//we save the mappings in the destination MappingStore too
			foreach(Mapping mapping in mappingStoreSrc.Mappings)
			{
				if(mapping.Exists)
				{
					Mapping mappingDest = new Mapping(mapping.DestValue,mapping.SrcValue,mapping.Property);
					mappingStoreDest.Mappings.Add(mappingDest);
				}
			}
			
			mappingStoreDest.Save();
			
			//we check if the 2 MappingStore are synchronized
			if(mappingStoreSrc.Mappings.Count != mappingStoreDest.Mappings.Count)
			{
				throw new Exception("The mapping store are not synchronized");
			}
		}
		
		private static bool SynchronizeItem(Category categoriesrc,Category CategoryDest,SyncDirection direction,MappingStore mappingStore)
		{
			bool synchronized = true;
			//we get their respective parent_id
			Mapping parentMapping = mappingStore.GetMapping(categoriesrc.parent_id,CategoryDest.parent_id,"categories_id");
			
			if(!categoriesrc.Equals(CategoryDest))
			{
				synchronized = false;
				
				//something changed since the last synchronization
				//we update the source Category
				if(parentMapping != null)
				{
					//we create variable to ho hold the destination Category_id and parent_id
					string old_Category = categoriesrc.categories_id;
					string old_parent_id = categoriesrc.parent_id;
					string old_parent_category_id = categoriesrc.parent_category_id;
					
					categoriesrc.categories_id = CategoryDest.categories_id;
					categoriesrc.parent_id = parentMapping.DestValue;
					categoriesrc.parent_category_id = parentMapping.DestValue;
				}
				else
				{
					if(categoriesrc.parent_id != "0")
					{
						//Category has been moved
						categoriesrc.categories_id = CategoryDest.categories_id;
						bool found = false;
						foreach(Mapping map in mappingStore.Mappings)
						{
							if(map.SrcValue == categoriesrc.parent_id)
							{
								categoriesrc.parent_id = map.DestValue;
								categoriesrc.parent_category_id = map.DestValue;
								found = true;
							}
						}
						if(found)
						{
							synchronized = CategoryDest.Store.MoveItem(categoriesrc.categories_id,categoriesrc.parent_id);
						}
						else
						{
							throw new Exception("It seems that the Category " + categoriesrc.categories_name + " has been moved, but the application could not find a mapping for the new parent Category");
						}
					}
					else
					{
						//root categories
						//we create variable to ho hold the destination Category_id and parent_id
						string old_Category = CategoryDest.categories_id;
						
						categoriesrc.categories_id = CategoryDest.categories_id;
						categoriesrc.parent_id = "0";
						categoriesrc.parent_category_id = "0";
					}
				}
				
				var languages = CategoryDest.Store.site.GetLanguageStore().LoadItems();
				
				Dictionary<string, string> _meta_descriptions = new Dictionary<string, string>();
				Dictionary<string, string> _meta_keywordss = new Dictionary<string, string>();
				Dictionary<string, string> _page_titles = new Dictionary<string, string>();
				Dictionary<string, string> _categories_names = new Dictionary<string, string>();
				Dictionary<string, string> _categories_urls = new Dictionary<string, string>();
				
				MappingStore _mappingStore = new MappingStore(categoriesrc.Store.site,CategoryDest.Store.site,"languages_id");
				foreach(Mapping mapping in _mappingStore.Mappings)
				{
					_meta_descriptions["meta_description[" + mapping.DestValue + "]"] = categoriesrc.meta_descriptions["meta_description[" + mapping.SrcValue + "]"];
					_meta_keywordss["meta_keywords[" + mapping.DestValue + "]"] = categoriesrc.meta_keywordss["meta_keywords[" + mapping.SrcValue + "]"];
					_page_titles["page_title[" + mapping.DestValue + "]"] = categoriesrc.page_titles["page_title[" + mapping.SrcValue + "]"];
					_categories_names["categories_name[" + mapping.DestValue + "]"] = categoriesrc.categories_names["categories_name[" + mapping.SrcValue + "]"];
					_categories_urls["categories_ur[" + mapping.DestValue + "]"] = categoriesrc.categories_urls["categories_ur[" + mapping.SrcValue + "]"];
				}
				
				categoriesrc.meta_descriptions = _meta_descriptions;
				categoriesrc.meta_keywordss = _meta_keywordss;
				categoriesrc.page_titles = _page_titles;
				categoriesrc.categories_names = _categories_names;
				categoriesrc.categories_urls = _categories_urls;
				
				synchronized = CategoryDest.Store.UpdateItem(categoriesrc);
			}
			else
			{
				if(parentMapping == null && categoriesrc.parent_id != "0")
				{
					//Category has been moved
					categoriesrc.categories_id = CategoryDest.categories_id;
					bool found = false;
					foreach(Mapping map in mappingStore.Mappings)
					{
						if(map.SrcValue == categoriesrc.parent_id)
						{
							categoriesrc.parent_id = map.DestValue;
							categoriesrc.parent_category_id = map.DestValue;
							found = true;
						}
					}
					if(found)
					{
						synchronized = CategoryDest.Store.MoveItem(categoriesrc.categories_id,categoriesrc.parent_id);
					}
					else
					{
						throw new Exception("It seems that the Category " + categoriesrc.categories_name + " has been moved, but the application could not find a mapping for the new parent Category");
					}
				}
			}
			
			return synchronized;
		}
		
		private static string UploadItem(Category[] categoriesSrc,CategoryStore CategoryStoreDest,Category Category, string parentId,MappingStore mappingStore)
		{
			string remoteId = string.Empty;
			
			Category.parent_id = parentId;
			remoteId = CategoryStoreDest.InsertItem(Category);
			
			return remoteId;
		}
		
		private static bool UploadItemRecursive(Category[] categoriesSrc,CategoryStore CategoryStoreDest,Category Category, string parentId,MappingStore mappingStore)
		{
			string remoteId = string.Empty;
			string old_parent_id = Category.parent_id;
			Category.parent_id = parentId;
			Category.parent_category_id = parentId;
			Dictionary<string, string> meta_descriptions = Category.meta_descriptions;
			Dictionary<string, string> meta_keywordss = Category.meta_keywordss;
			Dictionary<string, string> page_titles = Category.page_titles;
			Dictionary<string, string> categories_names = Category.categories_names;
			Dictionary<string, string> categories_urls = Category.categories_urls;
			
			var languages = CategoryStoreDest.site.GetLanguageStore().LoadItems();
			
			Dictionary<string, string> _meta_descriptions = new Dictionary<string, string>();
			Dictionary<string, string> _meta_keywordss = new Dictionary<string, string>();
			Dictionary<string, string> _page_titles = new Dictionary<string, string>();
			Dictionary<string, string> _categories_names = new Dictionary<string, string>();
			Dictionary<string, string> _categories_urls = new Dictionary<string, string>();
			
			MappingStore _mappingStore = new MappingStore(Category.Store.site,CategoryStoreDest.site,"languages_id");
			foreach(Mapping mapping in _mappingStore.Mappings)
			{
				_meta_descriptions["meta_description[" + mapping.DestValue + "]"] = Category.meta_descriptions["meta_description[" + mapping.SrcValue + "]"];
				_meta_keywordss["meta_keywords[" + mapping.DestValue + "]"] = Category.meta_keywordss["meta_keywords[" + mapping.SrcValue + "]"];
				_page_titles["page_title[" + mapping.DestValue + "]"] = Category.page_titles["page_title[" + mapping.SrcValue + "]"];
				_categories_names["categories_name[" + mapping.DestValue + "]"] = Category.categories_names["categories_name[" + mapping.SrcValue + "]"];
				_categories_urls["categories_ur[" + mapping.DestValue + "]"] = Category.categories_urls["categories_ur[" + mapping.SrcValue + "]"];
			}
			
			Category.meta_descriptions = _meta_descriptions;
			Category.meta_keywordss = _meta_keywordss;
			Category.page_titles = _page_titles;
			Category.categories_names = _categories_names;
			Category.categories_urls = _categories_urls;
			
			remoteId = CategoryStoreDest.InsertItem(Category);
			
			Category.parent_id = old_parent_id;
			Category.parent_category_id = old_parent_id;
			Category.meta_descriptions = meta_descriptions;
			Category.meta_keywordss = meta_keywordss;
			Category.page_titles = page_titles;
			Category.categories_names = categories_names;
			Category.categories_urls = categories_urls;
			
			if(remoteId != string.Empty)
			{
				CategoryStoreDest.PersistItem(Category);
				
				Mapping mapping= new Mapping(Category.categories_id,remoteId,"categories_id");
				mappingStore.Mappings.Add(mapping);
				
				foreach (Category categorie in categoriesSrc)
				{
					if (categorie.parent_id == Category.categories_id)
					{
						UploadItemRecursive(categoriesSrc,CategoryStoreDest,categorie,remoteId,mappingStore);
					}
				}
				
				return true;
			}
			else
			{
				return false;
			}
		}
	}
}
